﻿using System;
using System.IO;
using System.Xml;

namespace SPSource
{
    public class FeatureManager
    {
        private static XmlDocument LoadFeatureXml(string featureFilePath)
        {
            XmlDocument featureXmlDoc = new XmlDocument();
            featureXmlDoc.Load(featureFilePath);

            XmlNamespaceManager nsmgr = new XmlNamespaceManager(featureXmlDoc.NameTable);
            foreach (XmlAttribute attr in featureXmlDoc.DocumentElement.Attributes)
            {
                if (attr.Name == "xmlns")
                {
                    nsmgr.AddNamespace("wss", attr.Value);
                    break;
                }
            }
            if (!ValidFeatureXml(featureXmlDoc))
                Utility.WriteError(String.Format("Feature file is not valid! {0}", featureFilePath));

            return featureXmlDoc;
        }

        private static XmlDocument LoadFeatureXml(FileInfo file)
        {
            string featureFilePath = GetFeatureFileFromFileLocation(file).FullName;
            return LoadFeatureXml(featureFilePath);
        }

        private static bool ValidFeatureXml(XmlDocument featureXmlDoc)
        {
            return (featureXmlDoc.DocumentElement.Name.Equals("Feature"));
        }

        private static void SaveFeatureXml(FileInfo file, XmlDocument featureXmlDoc)
        {            
            try
            {
                featureXmlDoc.Save(GetFeatureFileFromFileLocation(file).FullName);
            }
            catch (UnauthorizedAccessException ex)
            {
                Utility.WriteError("Failed to update Feature File, please ensure that your feature.xml file is checked out in source control and not Read-only: " + file.FullName);
            }
            catch (Exception ex)
            {
                Utility.WriteError("Failed to update Feature File: " + file.FullName);
            }
        }

        public static void AddElementManifestToFeature(FileInfo elementManifestFile)
        {
            FileInfo featureFile = GetFeatureFileFromFileLocation(elementManifestFile);
            if (featureFile != null)
            {
                XmlDocument featureXmlDoc = LoadFeatureXml(featureFile.FullName);
                string elementManifestRelativeLocation = GetRelativePathToFeatureFolder(featureFile, elementManifestFile);
                //need to check whether it exists already
                bool elementManifestExists = false;

                if (featureXmlDoc.DocumentElement["ElementManifests"] == null)
                    featureXmlDoc.DocumentElement.AppendChild(featureXmlDoc.CreateElement("ElementManifests",
                                                                                          Constants.SharePointXmlNamespace));

                foreach (XmlNode elementManifest in featureXmlDoc.DocumentElement["ElementManifests"].ChildNodes)
                {
                    if ((elementManifest.Name == "ElementManifest") &&
                        (elementManifest.Attributes["Location"].Value.ToString() == elementManifestRelativeLocation))
                    {
                        elementManifestExists = true;
                        break;
                    }
                }

                //add if doesn't exist
                if (!elementManifestExists)
                {
                    XmlNode elementManifest = featureXmlDoc.CreateElement("ElementManifest",
                                                                          Constants.SharePointXmlNamespace);

                    XmlAttribute locationAttr = featureXmlDoc.CreateAttribute("Location");
                    locationAttr.Value = elementManifestRelativeLocation;
                    elementManifest.Attributes.Append(locationAttr);

                    featureXmlDoc.DocumentElement["ElementManifests"].AppendChild(elementManifest);
                }
                SaveFeatureXml(featureFile, featureXmlDoc);
            }
        }

        public static void AddElementFileToFeature(FileInfo elementFile)
        {
            FileInfo featureFile = GetFeatureFileFromFileLocation(elementFile);
            try
            {
                XmlDocument featureXmlDoc = LoadFeatureXml(featureFile.FullName);
                string elementFileRelativeLocation = GetRelativePathToFeatureFolder(featureFile, elementFile);
                //need to check whether it exists already
                bool elementFileExists = false;

                if (featureXmlDoc.DocumentElement["ElementManifests"] == null)
                    featureXmlDoc.DocumentElement.AppendChild(featureXmlDoc.CreateElement("ElementManifests", Constants.SharePointXmlNamespace));

                foreach (XmlNode elementManifest in featureXmlDoc.DocumentElement["ElementManifests"].ChildNodes)
                {
                    if ((elementManifest.Name == "ElementFile") && (elementManifest.Attributes["Location"].Value.ToString() == elementFileRelativeLocation))
                    {
                        elementFileExists = true;
                        break;
                    }
                }

                //add if doesn't exist
                if (!elementFileExists)
                {
                    XmlNode elementFileNode = featureXmlDoc.CreateElement("ElementFile", Constants.SharePointXmlNamespace);

                    XmlAttribute locationAttr = featureXmlDoc.CreateAttribute("Location");
                    locationAttr.Value = elementFileRelativeLocation;
                    elementFileNode.Attributes.Append(locationAttr);

                    featureXmlDoc.DocumentElement["ElementManifests"].AppendChild(elementFileNode);
                }
                SaveFeatureXml(featureFile, featureXmlDoc);
            }
            catch(UnauthorizedAccessException ex)
            {
                Utility.WriteError("Failed to update Feature File, please ensure that your feature.xml file is checked out in source control and not Read-only: " + featureFile.FullName);
            }
            catch (Exception ex)
            {
                Utility.WriteError("Failed to update Feature File: " + featureFile.FullName);
            }
        }

        private static string GetRelativePathToFeatureFolder(FileInfo featureFile, FileInfo elementFile)
        {
            return elementFile.FullName.Replace(String.Format("{0}\\",featureFile.Directory.FullName), string.Empty);
        }

        public static Guid GetFeatureIdFromFileLocation(FileInfo file)
        {
            XmlDocument featureXmlDoc = LoadFeatureXml(file);
            return new Guid(featureXmlDoc.DocumentElement.Attributes["Id"].Value.ToString());
        }

        public static FileInfo GetFeatureFileFromFileLocation(FileInfo file)
        {
            DirectoryInfo featureDirectory = Utility.CurrentFeatureRootDirectory(file);
            FileInfo[] featureFiles = featureDirectory.GetFiles("feature.xml");
            //NOTE: assumption that first feature file used
            return featureFiles[0];
        }

        /// <summary>
        /// Validates that the spsource file generating the list template is only one directory
        /// deep in feature, any more than one level creates an invalid feature
        /// </summary>
        /// <param name="spSourceFileDirectory"></param>
        /// <returns></returns>
        public static bool IsValidListSPSourceFileLocation(DirectoryInfo spSourceFileDirectory)
        {
            DirectoryInfo featureDirectory = Utility.CurrentFeatureRootDirectory(spSourceFileDirectory);
            return featureDirectory.Equals(spSourceFileDirectory);
        }
    }
}
